<template>
  <el-container class="fm2-container">
    <el-main class="fm2-main">
      <el-container>
        <el-container class="center-container" direction="vertical">
          <el-header class="btn-bar" style="height: 45px;">
            <slot name="action">
            </slot>
            <el-button type="text" size="medium" icon="el-icon-upload2" :disabled="true">
              {{ $t('bpmn.actions.import') }}
            </el-button>
            <el-button type="text" size="medium" icon="el-icon-download" @click="handleExportXmlAction">
              {{ $t('bpmn.actions.importXml') }}
            </el-button>
            <el-button type="text" size="medium" icon="el-icon-download" @click="handleExportSvgAction">
              {{ $t('bpmn.actions.importSvg') }}
            </el-button>
            <el-button type="text" size="medium" icon="el-icon-tickets" @click="xmlVisible = !xmlVisible">
              {{ $t('bpmn.actions.preview') }}
            </el-button>
            <el-button type="text" size="medium" icon="el-icon-delete" @click="handleClear">
              {{ $t('bpmn.actions.clear') }}
            </el-button>
            <el-button type="text" size="medium" icon="el-icon-document">{{ $t('bpmn.actions.save') }}</el-button>
          </el-header>
          <el-main>
            <div class="containers" :style="{height: getContainerHeight}">
              <div class="canvas" ref="canvas"/>

              <el-dialog :visible.sync="xmlVisible" title="XML" :fullscreen="false" top="10vh">
                <vue-ace-editor v-model="process.xml"
                                @init="editorInit"
                                lang="xml"
                                theme="chrome"
                                width="100%"
                                height="400"
                                :options="{wrap: true, readOnly: true}">
                </vue-ace-editor>
                <span slot="footer" class="dialog-footer">
                  <el-button icon="el-icon-document" v-clipboard:copy="process.xml"
                             v-clipboard:success="onCopy">复 制</el-button>
                  <el-button icon="el-icon-close" type="primary" @click="xmlVisible = false">关闭</el-button>
                </span>
              </el-dialog>

            </div>
          </el-main>
        </el-container>

        <el-aside class="widget-config-container">
          <el-container>
            <el-header height="45px">
              <div class="config-tab" :class="{active: configTab=='node'}"
                   @click="handleConfigSelect('node')">节点属性
              </div>
              <div class="config-tab" :class="{active: configTab=='process'}"
                   @click="handleConfigSelect('process')">流程属性
              </div>
            </el-header>
            <el-main class="config-content">
              <node-property-panel v-if="bpmnModeler && configTab=='node'"
                                   :modeler="bpmnModeler"/>
              <process-property-panel v-if="configTab=='process'"
                                      :process-data="process"></process-property-panel>
            </el-main>
          </el-container>
        </el-aside>
      </el-container>
    </el-main>
    <el-footer height="30px" style="font-weight: 600;">
      Powered by <a target="_blank" href="https://gitee.com/xiaofanger/vue-bpmn-flowable">vue-bpmn-flowable</a>
    </el-footer>
  </el-container>

</template>

<script>
// bpmn-js 设计器
import BpmnModeler from "bpmn-js/lib/Modeler";
// 对flowable的扩展
import flowableExtensionModule from 'jp-flowable-bpmn-moddle/lib';
import flowableModdle from "jp-flowable-bpmn-moddle/resources/flowable";

import NodePropertyPanel from "./NodePropertyPanel"; // 属性面板
import ProcessPropertyPanel from "./ProcessPropertyPanel";

import BpmData from "./BpmData";
import VueAceEditor from 'vue2-ace-editor'

export default {

  props: {
    processName: {
      type: String,
      default: '流程1567044459787',
    },
    processKey: {
      type: String,
      default: 'process1567044459787',
    },
    processDescription: {
      type: String,
      default: '描述',
    },
  },

  components: {
    NodePropertyPanel,
    ProcessPropertyPanel,
    VueAceEditor
  },

  data() {
    return {
      bpmnModeler: null,
      process: {
        name: this.processName,
        id: this.processKey,
        description: this.processDescription,
        xml: '',
        svg: ''
      },
      configTab: 'node',
      nodeProcessSelect: null,

      xmlVisible: false,
      element: null,
      bpmData: new BpmData(),
    };
  },

  methods: {
    /**
     * bind SVG element height.
     */
    getContainerHeight() {
      return (document.body.offsetHeight - 75) + 'px'
    },
    /**
     * init ace editor.
     */
    editorInit: function () {
      require('brace/ext/language_tools') //language extension prerequsite...
      require('brace/mode/xml')    //language
      require('brace/theme/chrome')
    },
    /**
     * init
     */
    createNewDiagram() {
      // 初始化XML文本
      this.process.xml = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
                   xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
                   targetNamespace="http://bpmn.io/schema/bpmn"
                   xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="${this.processKey}" name="${this.processName}">
    <bpmn2:documentation>${this.processDescription}</bpmn2:documentation>
    <bpmn2:startEvent id="StartEvent_01ydzqe" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="process1567044459787">
      <bpmndi:BPMNShape id="StartEvent_01ydzqe_di" bpmnElement="StartEvent_01ydzqe">
        <dc:Bounds x="242" y="212" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="247" y="263" width="25" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>`;
      // 将字符串转换成图显示出来
      this.bpmnModeler.importXML(this.process.xml, err => {
        if (err) {
          console.error(err);
        } else {
          this.adjustPalette();
        }
      });
    },

    // 调整左侧工具栏排版
    adjustPalette() {
      try {
        // 获取 bpmn 设计器实例
        const canvas = this.$refs.canvas;
        const djsPalette = canvas.children[0].children[1].children[4];
        const djsPalStyle = {
          width: "130px",
          padding: "5px",
          background: "white",
          left: "20px",
          borderRadius: 0
        };
        for (var key in djsPalStyle) {
          djsPalette.style[key] = djsPalStyle[key];
        }
        const palette = djsPalette.children[0];
        const allGroups = palette.children;
        allGroups[0].style["display"] = "none";
        // 修改控件样式
        for (var gKey in allGroups) {
          const group = allGroups[gKey];
          for (var cKey in group.children) {
            const control = group.children[cKey];
            const controlStyle = {
              display: "flex",
              justifyContent: "flex-start",
              alignItems: "center",
              width: "100%",
              padding: "5px"
            };
            if (
                control.className &&
                control.dataset &&
                control.className.indexOf("entry") !== -1
            ) {
              const controlProps = this.bpmData.getControl(
                  control.dataset.action
              );
              control.innerHTML = `<div style='font-size: 14px;font-weight:500;margin-left:15px;'>${
                  controlProps["title"]
              }</div>`;
              if (controlProps['tooltip']) {
                control.title = controlProps['tooltip'];
              }
              for (var csKey in controlStyle) {
                control.style[csKey] = controlStyle[csKey];
              }
            }
          }
        }
      } catch (e) {
        console.log(e);
      }
    },

    // 当图发生改变的时候会调用这个函数，这个data就是图的xml
    setEncoded(type, data) {
      // 把xml转换为URI，下载要用到的
      const encodedData = encodeURIComponent(data);
      // decodeURIComponent(data) 解码
      if (data) {
        if (type === 'XML') {
          // 获取到图的xml，保存就是把这个xml提交给后台
          this.process.xml = data;
          return {
            filename: this.process.name + '.xml',
            href: "data:application/bpmn20-xml;charset=UTF-8," + encodedData,
            data: data
          }
        }
        if (type === 'SVG') {
          this.process.svg = data;
          return {
            filename: this.process.name + '.svg',
            href: "data:application/text/xml;charset=UTF-8," + encodedData,
            data: data
          }
        }
      }
    },
    /**
     * 导出BPMN XML文件
     */
    handleExportXmlAction() {
      const _this = this;
      this.bpmnModeler.saveXML({format: true}, function (err, xml) {
        if (err) {
          console.error(err);
        }
        let {filename, href} = _this.setEncoded('XML', xml);
        if (href && filename) {
          let a = document.createElement('a');
          a.download = filename; //指定下载的文件名
          a.href = href; //  URL对象
          a.click(); // 模拟点击
          URL.revokeObjectURL(a.href); // 释放URL 对象
        }
      });
    },
    /**
     * 导出BPMN SVG文件
     */
    handleExportSvgAction() {
      const _this = this;
      this.bpmnModeler.saveSVG(function (err, svg) {
        if (err) {
          console.error(err);
        }
        let {filename, href} = _this.setEncoded('SVG', svg);
        if (href && filename) {
          let a = document.createElement('a');
          a.download = filename;
          a.href = href;
          a.click();
          URL.revokeObjectURL(a.href);
        }
      });
    },
    /**
     * 清空设计器内容
     */
    handleClear() {
      this.createNewDiagram();
    },
    /**
     * 复制内容到剪切板成功回调
     */
    onCopy() {
      this.$message.success('内容复制成功');
    },
    /**
     * 配置Tab切换
     */
    handleConfigSelect(value) {
      this.configTab = value;
    },
  },

  mounted() {
    const canvas = this.$refs.canvas;
    // 生成实例
    this.bpmnModeler = new BpmnModeler({
      container: canvas, // 渲染容器
      width: 'sting|number', // 查看器宽度，不写默认容器宽
      height: 'sting|number', // 查看器高度，不写默认容器高
      // modules: [], //自定义且需要覆盖默认扩展包的模块列表 <didi.Module>[]
      additionalModules: [
        flowableExtensionModule
      ], // 自定义且与默认扩展包一起使用的模块列表 <didi.Module>[]
      moddleExtensions: {
        flowable: flowableModdle
      } // 需要用的扩展包
    });

    // 监听流程图改变事件
    const _this = this;
    this.bpmnModeler.on("commandStack.changed", () => {
      _this.bpmnModeler.saveSVG({format: true}, function (err, svg) {
        _this.setEncoded('SVG', err ? null : svg);
      });
      _this.bpmnModeler.saveXML({format: true}, function (err, xml) {
        _this.setEncoded('XML', err ? null : xml);
      });
    });

    // 新增流程定义
    this.createNewDiagram();


//     /**
//      * 返回name对应的模块实例
//      * @param { string } name 模块名
//      * @param { boolean } strict 启用严格模式。false：缺少的模块解析为null返回；true：抛出异常
//      */
//     this.bpmnModeler.get(name, strict);
//
// // 创建空白流程图
// // 内部调用了importXML方法，读取内部的默认xml字符串
//     this.bpmnModeler.createDiagram();
//
// // 将图形dom挂载到目标节点
//     this.bpmnModeler.attachTo(parentNode);
//
// // 清空
//     this.bpmnModeler.clear()
//
// // 销毁
//     this.bpmnModeler.destroy()
//
// // 脱离dom
//     this.bpmnModeler.detach()
//
// // 获取流程定义
//     this.bpmnModeler.getDefinitions()
//
// // 获取扩展功能模块列表
//     this.bpmnModeler.getModules()
//
//     /**
//      * 导入解析的定义并呈现BPMN 2.0图。完成后，查看器将结果报告回给提供的回调函数（错误，警告）
//      * @param { ModdleElement<Definitions> } definitions 模块名
//      * @param { ModdleElement<BPMNDiagram>|string } [bpmnDiagram] 要呈现的BPMN图或图的ID（如果未提供，将呈现第一个）
//      */
//     this.bpmnModeler.importDefinitions(definitions, bpmnDiagram)
//
//     /**
//      * 导入xml（字符串形式），返回导入结果
//      * 后续会取消传入回调函数的方式
//      * 推荐使用async/await或者链式调用
//      * @param { string } xml 流程图xml字符串
//      * @param { Promise } callback 回调函数，出错时返回{ warnings，err }
//      */
//     this.bpmnModeler.importXML(xml, callback)
//
// // 注销事件监听器
//     this.bpmnModeler.off(eventName, callback)
//
// // 注册事件监听，同名将删除以前的监听器，privorty可不传，程序会自动替换回调函数
//     this.bpmnModeler.on(eventName, priority, callback, target)
//
// // em。。。不了解
//     this.bpmnModeler.open()
//
// // 保存为svg文件，与importXML方法一样，后续会取消传入callback的方式
//     this.bpmnModeler.saveSVG(callback)
//
// // 保存为xml文件，与importXML方法一样，后续会取消传入callback的方式
//     this.bpmnModeler.saveXML(callback)
  },
  watch: {
    'process.xml': {
      handler(val) {
        this.$nextTick(() => {
          //
        })
      }
    }
  }
};
</script>

<style lang="scss">
/*左边工具栏以及编辑节点的样式*/
@import "~bpmn-js/dist/assets/diagram-js.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";

.containers {
  /*position: absolute;*/
  background-color: #ffffff;
  width: 100%;
  height: 100%;

  .canvas {
    width: 100%;
    height: 100%;
  }

  .panel {
    position: absolute;
    right: 0;
    top: 50px;
    width: 300px;
  }

  .bjs-powered-by {
    display: none;
  }

  .toolbar {
    position: absolute;
    top: 0;
    right: 320px;
    height: 40px;
    width: 600px;
    border: 1px solid red;

    a {
      text-decoration: none;
      margin: 5px;
      color: #409eff;
    }
  }
}
</style>
